var TestFixture = function TestFixture(fixturename) {
  
  Array.prototype.contains =
  function(testString) {
    return new RegExp('^(' + this.join('|') + ')$').test(testString);
  };

  var Test = function Test(name, test, fixture) { 
    this.name = name;
    this.test = test;
    this.fixture = fixture;
    this.result = [];
    
    this.assertIsTrue = function(actual, message) {
      if (!actual) {
        throw(["AssertIsTrueFailure", "", message]);
      }
    };
    
    this.assertAreEqual = function(expected, actual, message) {
      if (expected != actual) {
        throw(["AssertEqualityFailure", expected + " != " + actual, message]);
      }
    };
    
    this.assertAreNotEqual = function(expected, actual, message) {
      if (expected == actual) {
        throw(["AssertInEqualityFailure", expected + " == " + actual, message]);
      }
    };
    
    this.runTest = function() { this.test(); };
        
  };

  var name = fixturename;
  var tests = [];
  var testSetup, fixtureSetup, testTeardown, fixtureTeardown;

  this.addTestSetup = function(setup) { testSetup = setup; };

  this.addFixtureSetup =  function(setup) { fixtureSetup = setup; };

  this.addTestTeardown = function(teardown) { testTeardown = teardown; };

  this.addFixtureTeardown =  function(teardown) { fixtureTeardown = teardown; };

  this.addTest = function(name, test) { 
    tests.push(new Test(name, test, this));
  };

  this.runTests = function(testnames) {
    if (fixtureSetup !== undefined) {
      fixtureSetup();
    }

    for(var i = 0;i<tests.length;i++) {
      if (testSetup !== undefined) {
        testSetup();
      }

      try {
        if ( testnames !== undefined && !testnames.contains(tests[i].name) )  {
          tests[i].result = ["Ignore"];
        }
        else {
          tests[i].runTest();
          tests[i].result = ["Pass"];
        }
      }
      catch(err) {
        if (    err[0] == "AssertEqualityFailure"  ||
                err[0] == "AssertInEqualityFailure" ||
                err[0] == "AssertIsTrueFailure" ) {
          tests[i].result = ["Fail", err[1], err[2]];
        }
        else {
          tests[i].result = ["Error", err, "Line: " + err.lineNumber ];
        }
        Logger.log("£££");
        Logger.log(err);
      }

      if (testTeardown !== undefined) {
        testTeardown();
      }
          
          
    }
        
        if (fixtureTeardown !== undefined) {
      fixtureTeardown();
    }

    return this; 
  };
  
  this.getResultsSummary = function getResultsSummary() {
    
    var passed = 0, failed = 0, errored = 0, ignored = 0;

    for(var i = 0;i<tests.length;i++) {
      switch (tests[i].result[0]) {
        case 'Pass': ++passed; break; 
        case 'Fail': ++failed; break; 
        case 'Ignore': ++ignored; break; 
        case 'Error': ++errored; break;
      }
    }

    return { 
      'passed' : passed,
      'failed' : failed,
      'errored' : errored,
      'ignored' : ignored,
      'total' : + tests.length
      };
       
  };    
      
  var getDateTimeString = function getDateTimeString(timezone) {
   //Script throws a permissions error when executed from a personal account as opposed to a google apps account
   //var timeZone = ( timeZone || "GMT" );
   //return Utilities.formatDate(new Date(), timeZone, "dd-MM-yyyy HH:mm:ss z");
   return new Date().toUTCString();
  };
    
  var getCellStyle = function getCellStyle(testResult) {
      var cellStyle = "style='background-color:";
      switch(testResult) {
        case 'Pass':
          cellStyle = cellStyle + "green";
          break;
        case 'Ignore':
          cellStyle = cellStyle + "yellow";
          break;
        case 'Fail':
          cellStyle = cellStyle + "red";
          break;
        case 'Error':
          cellStyle = cellStyle + "cyan";
          break;
        default:
          cellStyle = cellStyle + "white";
          break;
      }
      return cellStyle;

  };

  var getTableRow = function getTableRow(testName, testResult) {
     var cellStyle = getCellStyle(testResult[0]);
     
     return "<tr>" +
      "<td>" + testName + "</td>" +
      "<td " + cellStyle + "'>" + testResult[0] + "</td>" +
      "<td>" + ( testResult[1] || '' ) + "</td>" + 
      "<td>" + ( testResult[2] || '' ) + "</td>" +
      "</tr>";
  };

  this.createTextTestReport = function createTextTestReport(timeZone) {
  
    var resultSummary = this.getResultsSummary();

    var results = 'Test Run: ' + name + ' ' + getDateTimeString(timeZone) + '\n' +
    resultSummary.passed + ' Passed\n' +
    resultSummary.failed + ' Failed\n' +
    resultSummary.errored + ' Errored\n' +
    resultSummary.ignored + ' Ignored\n' +
    resultSummary.total + ' Total\n';

    for(var i = 0;i<tests.length;i++) {
      results +=  tests[i].result + ' ' + tests[i].name + '\n';
    }
          
    return results;
  };
   
  this.createHtmlTestReport = function createHtmlTestReport(timeZone) {
    
    var tableRows = "";
    
    for(var i = 0;i<tests.length;i++) {
      tableRows = tableRows + getTableRow(tests[i].name, tests[i].result);
    }

    var resultSummary = this.getResultsSummary();
    var resultSummaryString = resultSummary.passed + ' Passed, ' + resultSummary.failed + ' Failed, ' + resultSummary.errored + ' Errored, ' + resultSummary.total + ' Total';
      
    var message = "<p>Test Run: " + name + ' ' + getDateTimeString(timeZone) + "</p>" +
    "<p>" + resultSummaryString + "</p>" +
    "<table border='1' width='100%'>" +
    "<tr><th>Name</th><th>Result</th><th>Details</th><th>Message</th></tr>" +
    tableRows +
    "</table>";

    return message;
  };
  
  
};
